import Fluent
import FluentMySQLDriver
import MySQLNIO
//
//  HistoryController.swift
//  hello
//
//  Created by 赵康 on 2024/11/25.
//
import Vapor

// 添加向下兼容的日期格式化辅助函数
private func formatDate(_ date: Date) -> String {
  let formatter = DateFormatter()
  formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'"
  formatter.timeZone = TimeZone(secondsFromGMT: 0)
  return formatter.string(from: date)
}

struct HistoryController: RouteCollection {
  // 系统补签用的音频和专辑ID
  private static let systemAudioID = UUID(uuidString: "00000000-0000-0000-0000-000000000000")!
  private static let systemAlbumID = UUID(uuidString: "00000000-0000-0000-0000-000000000000")!

  func boot(routes: any RoutesBuilder) throws {
    let history = routes.grouped("\(RouteEnum.histories.rawValue)")
    let protected = history.grouped(AuthMiddleware())

    protected.post("\(RouteEnum.create.rawValue)", ":audioID", use: create)
    protected.get("\(RouteEnum.fetch.rawValue)", use: fetchHistories)
    protected.delete("\(RouteEnum.delete.rawValue)", ":historyID", use: softDeleteHistory)
    protected.post("\(RouteEnum.batch.rawValue)", "\(RouteEnum.delete.rawValue)", use: batchSoftDeleteHistories)
    protected.get("\(RouteEnum.progress.rawValue)", ":audioID", use: getLatestProgress)
    protected.post("\(RouteEnum.update.rawValue)", ":audioID", use: update)
    protected.get("\(RouteEnum.unfinished.rawValue)", use: getUnfinishedAudios)
    protected.get("\(RouteEnum.audioCount.rawValue)", use: getAudioCount)
    protected.get("\(RouteEnum.recentCovers.rawValue)", use: getRecentCovers)
  }

  @Sendable
  func getLatestProgress(req: Request) async throws -> UpdateHistoryProgress {
    let userID = try req.authenticatedUserID
    guard let audioID = req.parameters.get("audioID", as: UUID.self) else {
      throw Abort(.badRequest, reason: "Invalid audio ID")
    }
    guard
      let latestHistory = try await History.query(on: req.db)
        .filter(\.$user.$id == userID)
        .filter(\.$audio.$id == audioID)
        .sort(\.$createdAt, .descending)
        .first()
    else {
      return UpdateHistoryProgress(progress: 0, listenedDuration: 0)
    }
    return UpdateHistoryProgress(
      progress: latestHistory.progress, listenedDuration: latestHistory.listenedDuration)
  }

  // MARK: - 创建听力记录
  @Sendable
  func create(req: Request) async throws -> HistoryResponse {
    let userID = try req.authenticatedUserID
    guard let audioID = req.parameters.get("audioID", as: UUID.self) else {
      throw Abort(.badRequest, reason: "Invalid audio ID")
    }
    let input = try req.content.decode(UpdateHistoryProgress.self)

    return try await req.db.transaction { database in
      // 1. 获取音频信息
      guard
        let audio = try await Audio.query(on: database)
          .with(\.$album)
          .filter(\.$id == audioID)
          .first()
      else {
        throw Abort(.notFound, reason: "音频不存在")
      }

      // 2. 查找最近30分钟内的历史记录
      let thirtyMinutesAgo = Date().addingTimeInterval(-30 * 60)
      let recentHistory = try await History.query(on: database)
        .filter(\.$user.$id == userID)
        .filter(\.$audio.$id == audioID)
        .filter(\.$lastListenedAt >= thirtyMinutesAgo)
        .sort(\.$createdAt, .descending)
        .first()

      // 3. 检查是否存在被软删除的该音频历史记录
      let softDeletedHistory = try await History.query(on: database)
        .filter(\.$user.$id == userID)
        .filter(\.$audio.$id == audioID)
        .filter(\.$deleteStatus == .softDeleted)
        .sort(\.$lastListenedAt, .descending)
        .first()

      if let existing = recentHistory {
        // 更新现有记录
        if input.progress >= 0.98 {
          if existing.listenStatus != ListenStatus.completed {
            existing.listenStatus = ListenStatus.completed
            existing.completedCount += 1
            existing.isFinished = true
          }
        } else {
          existing.isFinished = false
          if existing.completedCount > 0 {
            existing.listenStatus = ListenStatus.relistening
          } else {
            existing.listenStatus = ListenStatus.inProgress
          }
        }

        existing.progress = input.progress
        existing.lastListenedAt = Date()
        existing.listenedDuration += input.listenedDuration
        
        // 确保状态为normal
        existing.deleteStatus = .normal
        existing.deletedAt = nil

        try await existing.save(on: database)
        
        // 清理缓存
        try await invalidateRelatedCaches(
          audioID: audio.id!,
          albumID: audio.$album.id,
          userID: userID,
          req: req
        )
        
        // 检查今日学习时长是否达到目标，如果是则更新连续打卡并奖励积分
        // 获取今天的开始时间和结束时间
        let today = Calendar.mondayFirst.startOfDay(for: Date())
        let tomorrow = Calendar.mondayFirst.date(byAdding: .day, value: 1, to: today)!
        
        // 获取今日的学习记录总时长
        let histories = try await History.query(on: database)
          .filter(\.$user.$id == userID)
          .filter(\.$lastListenedAt >= today)
          .filter(\.$lastListenedAt < tomorrow)
          .filter(\.$listenedDuration > 0)
          .all()
        
        // 计算今日总听力时长
        let totalDuration = histories.reduce(0) { $0 + $1.listenedDuration }
        
        // 获取用户信息
        guard let user = try await User.find(userID, on: database) else {
          throw Abort(.notFound, reason: "用户不存在")
        }
        
        // 判断是否完成今日目标
        let isCompleted = totalDuration >= user.dailyGoal
        
        // 如果完成了目标，更新连续打卡并可能奖励积分
        if isCompleted {
          // 使用之前添加的方法更新连续打卡并奖励积分
          try await updateUserStreak(userID: userID, db: database)
        }

        return HistoryResponse(
          id: existing.id,
          progress: existing.progress,
          duration: existing.duration,
          isFinished: existing.isFinished,
          lastListenedAt: existing.lastListenedAt,
          createdAt: existing.createdAt,
          listenedDuration: existing.listenedDuration,
          deleteStatus: existing.deleteStatus,
          deletedAt: existing.deletedAt,
          listenStatus: existing.listenStatus,
          completedCount: existing.completedCount,
          audio: AudioSearchResponse(
            id: audio.id,
            title: audio.title,
            description: audio.description,
            duration: audio.duration,
            cover: audio.cover,
            author: audio.author,
            url: audio.url,
            size: audio.size,
            albumID: audio.$album.id,
            albumTitle: audio.album.title,
            albumCover: audio.album.cover,
            createdAt: audio.createdAt,
            updatedAt: audio.updatedAt
          )
        )
      } else if let softDeleted = softDeletedHistory {
        // 恢复软删除的记录
        softDeleted.progress = input.progress
        softDeleted.lastListenedAt = Date()
        softDeleted.listenedDuration += input.listenedDuration
        softDeleted.deleteStatus = .normal
        softDeleted.deletedAt = nil
        
        if input.progress >= 0.98 {
          softDeleted.isFinished = true
          softDeleted.listenStatus = .completed
          softDeleted.completedCount += 1
        } else {
          softDeleted.isFinished = false
          if softDeleted.completedCount > 0 {
            softDeleted.listenStatus = .relistening
          } else {
            softDeleted.listenStatus = .inProgress
          }
        }
        
        try await softDeleted.save(on: database)
        
        // 清理缓存
        try await invalidateRelatedCaches(
          audioID: audio.id!,
          albumID: audio.$album.id,
          userID: userID,
          req: req
        )
        
        // 检查今日学习时长是否达到目标
        let today = Calendar.mondayFirst.startOfDay(for: Date())
        let tomorrow = Calendar.mondayFirst.date(byAdding: .day, value: 1, to: today)!
        
        let histories = try await History.query(on: database)
          .filter(\.$user.$id == userID)
          .filter(\.$lastListenedAt >= today)
          .filter(\.$lastListenedAt < tomorrow)
          .filter(\.$listenedDuration > 0)
          .all()
        
        let totalDuration = histories.reduce(0) { $0 + $1.listenedDuration }
        guard let user = try await User.find(userID, on: database) else {
          throw Abort(.notFound, reason: "用户不存在")
        }
        
        let isCompleted = totalDuration >= user.dailyGoal
        if isCompleted {
          try await updateUserStreak(userID: userID, db: database)
        }
        
        return HistoryResponse(
          id: softDeleted.id,
          progress: softDeleted.progress,
          duration: softDeleted.duration,
          isFinished: softDeleted.isFinished,
          lastListenedAt: softDeleted.lastListenedAt,
          createdAt: softDeleted.createdAt,
          listenedDuration: softDeleted.listenedDuration,
          deleteStatus: softDeleted.deleteStatus,
          deletedAt: softDeleted.deletedAt,
          listenStatus: softDeleted.listenStatus,
          completedCount: softDeleted.completedCount,
          audio: AudioSearchResponse(
            id: audio.id,
            title: audio.title,
            description: audio.description,
            duration: audio.duration,
            cover: audio.cover,
            author: audio.author,
            url: audio.url,
            size: audio.size,
            albumID: audio.$album.id,
            albumTitle: audio.album.title,
            albumCover: audio.album.cover,
            createdAt: audio.createdAt,
            updatedAt: audio.updatedAt
          )
        )
      } else {
        // 获取最后一条历史记录
        let lastHistory = try await History.query(on: database)
          .filter(\.$user.$id == userID)
          .filter(\.$audio.$id == audioID)
          .sort(\.$createdAt, .descending)
          .first()

        // 确定新记录的状态
        let newStatus: ListenStatus
        let newCount: Int

        if input.progress >= 0.98 {
          newStatus = ListenStatus.completed
          newCount = (lastHistory?.completedCount ?? 0) + 1
        } else {
          if let lastHistory = lastHistory, lastHistory.completedCount > 0 {
            newStatus = ListenStatus.relistening
          } else {
            newStatus = ListenStatus.inProgress
          }
          newCount = lastHistory?.completedCount ?? 0
        }

        let history = History(
          userID: userID,
          audioID: audioID,
          progress: input.progress,
          duration: audio.duration,
          isFinished: input.progress >= 0.98,
          lastListenedAt: Date(),
          createdAt: Date(),
          listenedDuration: input.listenedDuration,
          listenStatus: newStatus,
          completedCount: newCount
        )

        try await history.save(on: database)

        // 检查今日学习时长是否达到目标
        let today = Calendar.mondayFirst.startOfDay(for: Date())
        let tomorrow = Calendar.mondayFirst.date(byAdding: .day, value: 1, to: today)!
        
        let histories = try await History.query(on: database)
          .filter(\.$user.$id == userID)
          .filter(\.$lastListenedAt >= today)
          .filter(\.$lastListenedAt < tomorrow)
          .filter(\.$listenedDuration > 0)
          .all()
        
        let totalDuration = histories.reduce(0) { $0 + $1.listenedDuration }
        guard let user = try await User.find(userID, on: database) else {
          throw Abort(.notFound, reason: "用户不存在")
        }
        
        let isCompleted = totalDuration >= user.dailyGoal
        if isCompleted {
          try await updateUserStreak(userID: userID, db: database)
        }

        return HistoryResponse(
          id: history.id,
          progress: history.progress,
          duration: history.duration,
          isFinished: history.isFinished,
          lastListenedAt: history.lastListenedAt,
          createdAt: history.createdAt,
          listenedDuration: history.listenedDuration,
          deleteStatus: history.deleteStatus,
          deletedAt: history.deletedAt,
          listenStatus: history.listenStatus,
          completedCount: history.completedCount,
          audio: AudioSearchResponse(
            id: audio.id,
            title: audio.title,
            description: audio.description,
            duration: audio.duration,
            cover: audio.cover,
            author: audio.author,
            url: audio.url,
            size: audio.size,
            albumID: audio.$album.id,
            albumTitle: audio.album.title,
            albumCover: audio.album.cover,
            createdAt: audio.createdAt,
            updatedAt: audio.updatedAt
          )
        )
      }
    }
  }

  @Sendable
  func update(req: Request) async throws -> History {
    let userID = try req.authenticatedUserID

    req.logger.info("开始更新用户 \(userID) 的历史记录")

    guard let audioID = req.parameters.get("audioID", as: UUID.self) else {
      req.logger.error("无效的音频ID")
      throw Abort(.badRequest, reason: "Invalid audio ID")
    }

    let input = try req.content.decode(UpdateHistoryProgress.self)
    req.logger.info("收到更新请求: 音频ID=\(audioID), 进度=\(input.progress), 听取时长=\(input.listenedDuration)秒")

    return try await req.db.transaction({ (database: Database) -> History in
      // 1. 获取最近的历史记录（不过滤deleteStatus）
      let history = try await History.query(on: database)
        .filter(\.$user.$id == userID)
        .filter(\.$audio.$id == audioID)
        .sort(\.$createdAt, .descending)
        .first()
      
      if let existingHistory = history {
        req.logger.info("找到现有历史记录: ID=\(existingHistory.id?.uuidString ?? "nil"), 进度=\(existingHistory.progress), 听取时长=\(existingHistory.listenedDuration)秒, 删除状态=\(existingHistory.deleteStatus.rawValue)")
      } else {
        req.logger.info("未找到现有历史记录")
      }
      
      // 检查是否存在被软删除的记录
      let softDeletedHistory = try await History.query(on: database)
        .filter(\.$user.$id == userID)
        .filter(\.$audio.$id == audioID)
        .filter(\.$deleteStatus == .softDeleted)
        .with(\.$audio)
        .sort(\.$lastListenedAt, .descending)
        .first()
      
      if let deletedHistory = softDeletedHistory {
        req.logger.info("找到软删除的历史记录: ID=\(deletedHistory.id?.uuidString ?? "nil"), 进度=\(deletedHistory.progress), 听取时长=\(deletedHistory.listenedDuration)秒")
      }
      
      // 如果没有任何记录，创建一个新的
      if history == nil && softDeletedHistory == nil {
        req.logger.info("没有现有记录，将创建新记录")
        // 获取音频信息
        guard
          let audio = try await Audio.query(on: database)
            .with(\.$album)
            .filter(\.$id == audioID)
            .first()
        else {
          req.logger.error("音频不存在: \(audioID)")
          throw Abort(.notFound, reason: "音频不存在")
        }
        
        req.logger.info("创建新历史记录，输入值: 进度=\(input.progress), 听取时长=\(input.listenedDuration)秒")
        
        let newHistory = History(
          userID: userID,
          audioID: audioID,
          progress: input.progress,
          duration: audio.duration,
          isFinished: input.progress >= 0.98,
          lastListenedAt: Date(),
          createdAt: Date(),
          listenedDuration: input.listenedDuration,
          listenStatus: input.progress >= 0.98 ? .completed : .inProgress,
          completedCount: input.progress >= 0.98 ? 1 : 0
        )
        
        try await newHistory.save(on: database)
        req.logger.info("创建新历史记录成功: 进度=\(newHistory.progress), 听取时长=\(newHistory.listenedDuration)秒")
        
        // 清理缓存
        try await invalidateRelatedCaches(
          audioID: audioID,
          albumID: audio.$album.id,
          userID: userID,
          req: req
        )
        
        return newHistory
      }
      
      // 优先使用正常记录，如果没有则恢复软删除的记录
      let recordToUpdate = history ?? softDeletedHistory!
      
      // 记录更新前的值
      req.logger.info("更新前的历史记录状态: 进度=\(recordToUpdate.progress), 听取时长=\(recordToUpdate.listenedDuration)秒, 删除状态=\(recordToUpdate.deleteStatus.rawValue)")
      
      // 如果是软删除状态，恢复为正常
      if recordToUpdate.deleteStatus == .softDeleted {
        recordToUpdate.deleteStatus = .normal
        recordToUpdate.deletedAt = nil
        req.logger.info("恢复软删除的记录为正常状态")
      }

      // 2. 使用与 create 相同的状态转换逻辑
      let newStatus: ListenStatus
      let newCount: Int
      let newIsFinished: Bool

      if input.progress >= 0.98 {
        newStatus = ListenStatus.completed
        if recordToUpdate.listenStatus != ListenStatus.completed {
          newCount = recordToUpdate.completedCount + 1
        } else {
          newCount = recordToUpdate.completedCount
        }
        newIsFinished = true
      } else {
        newIsFinished = false
        // 使用 completedCount 来判断是否应该是重听状态
        if recordToUpdate.completedCount > 0 {
          newStatus = ListenStatus.relistening
        } else {
          newStatus = ListenStatus.inProgress
        }
        newCount = recordToUpdate.completedCount
      }

      // 3. 更新记录
      // 注意：这里存储的是固定的 input.listenedDuration，而非累加值！
      recordToUpdate.progress = max(0, min(1, input.progress))
      recordToUpdate.lastListenedAt = Date()
      
      // 记录时长变化情况
      if input.listenedDuration < recordToUpdate.listenedDuration {
        req.logger.warning("警告：收到的时长值(\(input.listenedDuration)秒)小于当前记录的时长(\(recordToUpdate.listenedDuration)秒)，差值为\(recordToUpdate.listenedDuration - input.listenedDuration)秒")
        req.logger.info("保留原有较大的时长值")
      }
      
      // 修改前: recordToUpdate.listenedDuration = max(0, input.listenedDuration)
      // 修改后: 只接受更大的时长值，防止时长意外减少
      recordToUpdate.listenedDuration = max(recordToUpdate.listenedDuration, input.listenedDuration)
      recordToUpdate.listenStatus = newStatus
      recordToUpdate.completedCount = newCount
      recordToUpdate.isFinished = newIsFinished

      try await recordToUpdate.save(on: database)
      req.logger.info("更新后的历史记录状态: 进度=\(recordToUpdate.progress), 听取时长=\(recordToUpdate.listenedDuration)秒, 状态=\(recordToUpdate.listenStatus.rawValue), 完成次数=\(recordToUpdate.completedCount)")

      // 获取音频对象以便清理缓存
      if recordToUpdate.$audio.value == nil {
        try await recordToUpdate.$audio.load(on: database)
      }
      
      // 清理缓存
      try await invalidateRelatedCaches(
        audioID: recordToUpdate.$audio.id,
        albumID: try await recordToUpdate.audio.$album.id,
        userID: userID,
        req: req
      )
      
      // 检查是否今天的学习时长已达到目标，如果是则更新连续打卡并奖励积分
      // 获取今天的开始时间和结束时间
      let today = Calendar.mondayFirst.startOfDay(for: Date())
      let tomorrow = Calendar.mondayFirst.date(byAdding: .day, value: 1, to: today)!
      
      // 获取今日的学习记录总时长
      let histories = try await History.query(on: database)
        .filter(\.$user.$id == userID)
        .filter(\.$lastListenedAt >= today)
        .filter(\.$lastListenedAt < tomorrow)
        .filter(\.$listenedDuration > 0)
        .all()
      
      // 计算今日总听力时长
      let totalDuration = histories.reduce(0) { $0 + $1.listenedDuration }
      req.logger.info("用户 \(userID) 今日总学习时长: \(totalDuration)秒")
      
      // 获取用户信息
      guard let user = try await User.find(userID, on: database) else {
        throw Abort(.notFound, reason: "用户不存在")
      }
      
      // 判断是否完成今日目标
      let isCompleted = totalDuration >= user.dailyGoal
      req.logger.info("用户每日目标: \(user.dailyGoal)秒, 是否完成: \(isCompleted)")
      
      // 如果完成了目标，更新连续打卡并可能奖励积分
      if isCompleted {
        req.logger.info("用户已完成每日目标，更新连续打卡")
        // 使用CheckInController中的方法更新连续打卡并奖励积分
        try await updateUserStreak(userID: userID, db: database)
      }
      
      return recordToUpdate
    })
  }

  // 从CheckInController复制的方法，用于更新连续打卡并奖励积分
  private func updateUserStreak(userID: UUID, db: Database) async throws {
    let user = try await User.find(userID, on: db)
    guard let user = user else {
      throw Abort(.notFound)
    }

    // 获取今天的开始时间
    let today = Calendar.mondayFirst.startOfDay(for: Date())

    // 获取所有有效的打卡记录（包括补签）
    let allHistories = try await History.query(on: db)
      .filter(\.$user.$id == userID)
      .sort(\.$lastListenedAt, .descending)
      .all()

    // 按日期分组并累加时长
    var dailyListeningTime: [Date: Double] = [:]
    
    for history in allHistories {
      guard let date = history.lastListenedAt else { continue }
      let dayStart = Calendar.mondayFirst.startOfDay(for: date)
      let currentTotal = dailyListeningTime[dayStart] ?? 0
      dailyListeningTime[dayStart] = currentTotal + history.listenedDuration
    }
    
    // 筛选出达到每日目标的日期
    let completedDates = dailyListeningTime.filter { $0.value >= user.dailyGoal }.keys.sorted(by: >)

    // 如果没有打卡记录，重置连续打卡天数为0
    guard !completedDates.isEmpty else {
      user.currentStreak = 0
      try await user.save(on: db)
      return
    }

    // 计算连续打卡天数（从最近一次打卡开始往前数）
    var currentStreak = 1
    let calendar = Calendar.mondayFirst
    var checkDate = completedDates[0]

    // 从最新打卡日期往前检查每一天是否有打卡记录
    while true {
      let previousDate = calendar.date(byAdding: .day, value: -1, to: checkDate)!
      // 检查前一天是否有达到学习目标的打卡记录
      if completedDates.contains(where: { calendar.isDate($0, inSameDayAs: previousDate) }) {
        currentStreak += 1
        checkDate = previousDate
      } else {
        break
      }
    }

    // 检查当前是否还在连续打卡中
    let lastCheckInDate = completedDates[0]
    if let daysSinceLastCheckIn = calendar.dateComponents([.day], from: lastCheckInDate, to: today)
      .day
    {
      if daysSinceLastCheckIn > 1 {
        // 如果最后一次打卡不是昨天或今天，说明连续打卡已中断
        currentStreak = 0
      }
    }

    // 检查是否是新的一天的打卡，如果是则需要奖励积分
    let shouldAwardPoints = shouldAwardDailyPoints(user: user, today: today)
    
    // 如果需要奖励积分
    if shouldAwardPoints {
      // 增加10积分
      user.points += 10
      
      // 记录最后一次奖励积分的日期
      user.lastPointsAwardedDate = today
      
      // 增加总打卡天数
      user.totalCheckIns += 1
      
      // 创建积分历史记录
      try await createPointsHistory(
        userID: user.requireID(),
        amount: 10,
        type: .dailyCheckIn,
        description: "每日打卡奖励",
        db: db
      )
      
      // 检查是否达到里程碑奖励
      try await checkAndAwardMilestonePoints(user: user, db: db)
    }
    
    // 更新总打卡天数以匹配实际完成的打卡数量
    user.totalCheckIns = max(user.totalCheckIns, completedDates.count)
    user.currentStreak = currentStreak
    try await user.save(on: db)
  }
  
  // 检查是否应该奖励每日积分
  private func shouldAwardDailyPoints(user: User, today: Date) -> Bool {
    guard let lastAwardDate = user.lastPointsAwardedDate else {
      // 如果没有最后奖励日期，说明用户从未获得过积分奖励
      return true
    }
    
    // 检查最后一次奖励的日期是否是今天
    let calendar = Calendar.mondayFirst
    let lastAwardDay = calendar.startOfDay(for: lastAwardDate)
    let todayStart = calendar.startOfDay(for: today)
    
    // 如果最后一次奖励不是今天，则应该奖励积分
    return lastAwardDay != todayStart
  }
  
  // 检查并奖励里程碑积分
  private func checkAndAwardMilestonePoints(user: User, db: Database) async throws {
    // 里程碑天数和对应的奖励积分
    let milestones: [(days: Int, points: Int)] = [
      (7, 100),
      (30, 200),
      (60, 300),
      (100, 600),
      (200, 1000),
      (360, 2000)
    ]
    
    // 计算当前里程碑周期中的天数
    let currentCycleDays = user.totalCheckIns - user.lastMilestoneAwarded
    
    // 检查是否达到里程碑
    for milestone in milestones.sorted(by: { $0.days < $1.days }) {
      if currentCycleDays >= milestone.days {
        // 用户达到了新的里程碑
        
        // 奖励积分
        user.points += milestone.points
        
        // 更新最后里程碑奖励天数
        user.lastMilestoneAwarded += milestone.days
        
        // 保存用户信息
        try await user.save(on: db)
        
        // 创建积分历史记录
        try await createPointsHistory(
          userID: user.requireID(),
          amount: milestone.points,
          type: .milestone,
          description: "累计打卡\(milestone.days)天里程碑奖励",
          db: db
        )
        
        // 只奖励一个里程碑，然后退出
        // 如果用户一次性满足多个里程碑，会在下次打卡时奖励下一个
        break
      }
    }
  }
  
  // 创建积分历史记录的辅助方法
  private func createPointsHistory(
    userID: UUID,
    amount: Int,
    type: PointsHistoryType,
    description: String,
    db: Database
  ) async throws {
    let pointsHistory = PointsHistory(
      userID: userID,
      amount: amount,
      balance: try await getCurrentPointsBalance(userID: userID, db: db),
      type: type,
      description: description,
      createdAt: Date()
    )
    try await pointsHistory.save(on: db)
  }
  
  // 获取当前积分余额
  private func getCurrentPointsBalance(userID: UUID, db: Database) async throws -> Int {
    guard let user = try await User.find(userID, on: db) else {
      throw Abort(.notFound, reason: "用户不存在")
    }
    return user.points
  }

  private struct DBRawID: Decodable {
    let id: UUID
  }

  // MARK: - 获取听力记录列表
  @Sendable
  func fetchHistories(req: Request) async throws -> Response {
    let userID = try req.authenticatedUserID
    let input = try req.query.decode(FetchHistoriesRequest.self)

    guard let db = req.db as? MySQLDatabase else {
      throw Abort(.internalServerError, reason: "Database must be MySQL")
    }

    let lastestIdsQuery = db.sql().raw(
      """
      		SELECT h1.id
      		FROM histories h1
      		LEFT JOIN histories h2
      				ON h1.audio = h2.audio
      				AND h1.lastListenedAt < h2.lastListenedAt
      				AND h2.deleteStatus = 'normal'
      				AND h2.user = \(bind: userID)
      		WHERE h1.user = \(bind: userID)
      		AND h1.deleteStatus = 'normal'
      		AND h2.id IS NULL
      """)
    let results = try await lastestIdsQuery.all(decoding: DBRawID.self)
    let latestHistoryIds = results.map { $0.id }
    var query = History.query(on: req.db)
      .filter(\.$user.$id == userID)
      .filter(\.$deleteStatus == .normal)
      .filter(\.$id ~~ latestHistoryIds)  // 只查询最新的记录
      .filter(\.$audio.$id != Self.systemAudioID)  // 过滤掉系统音频
      .with(\.$audio) { audio in
        audio.with(\.$album)
      }
      .sort(\.$lastListenedAt, .descending)
    if let timeRange = input.timeRange {
      let now = Date()
      if timeRange != .all {
        let startDate: Date = {
          switch timeRange {
          case .week:
            return Calendar.mondayFirst.date(byAdding: .day, value: -7, to: now)!
          case .month:
            return Calendar.mondayFirst.date(byAdding: .month, value: -1, to: now)!
          case .halfYear:
            return Calendar.mondayFirst.date(byAdding: .month, value: -6, to: now)!
          case .all:
            return now
          }
        }()
        query = query.filter(\.$lastListenedAt >= startDate)
      }
    }

    // 处理搜索文本
    if let searchText = input.searchText?.trimmingCharacters(in: .whitespacesAndNewlines),
      !searchText.isEmpty
    {
      query =
        query
        .join(Audio.self, on: \History.$audio.$id == \Audio.$id)
        .join(Album.self, on: \Audio.$album.$id == \Album.$id)
        .group(.or) { group in
          group
            .filter(Audio.self, \.$title ~~ searchText)  // 搜索音频标题
            .filter(Audio.self, \.$description ~~ searchText)  // 搜索音频描述
            .filter(Audio.self, \.$author ~~ searchText)  // 搜索音频作者
            .filter(Album.self, \.$title ~~ searchText)  // 搜索专辑标题
        }
    }
    _ = try await query.count()

    // 按专辑分组的情况
    if input.groupBy == .album {
      // 获取所有记录用于分组
      let histories = try await query.all()

      // 转换为响应格式
      let historyResponses = histories.map { history in
        HistoryResponse(
          id: history.id,
          progress: history.progress,
          duration: history.duration,
          isFinished: history.isFinished,
          lastListenedAt: history.lastListenedAt,
          createdAt: history.createdAt,
          listenedDuration: history.listenedDuration,
          deleteStatus: history.deleteStatus,
          deletedAt: history.deletedAt,
          listenStatus: history.listenStatus,
          completedCount: history.completedCount,
          audio: AudioSearchResponse(
            id: history.audio.id,
            title: history.audio.title,
            description: history.audio.description,
            duration: history.audio.duration,
            cover: history.audio.cover,
            author: history.audio.author,
            url: history.audio.url,
            size: history.audio.size,
            albumID: history.audio.$album.id,
            albumTitle: history.audio.album.title,
            albumCover: history.audio.album.cover,
            createdAt: history.audio.createdAt,
            updatedAt: history.audio.updatedAt
          )
        )
      }

      // 按专辑分组
      let groupedHistories = Dictionary(grouping: historyResponses) { $0.audio.albumID }
        .map { (albumID, histories) in
          let firstHistory = histories[0]
          return HistoryGroupByAlbumResponse(
            albumID: albumID!,
            albumTitle: firstHistory.audio.albumTitle,
            albumCover: firstHistory.audio.albumCover,
            histories: histories
          )
        }

      return try await groupedHistories.encodeResponse(for: req)
    } else {
      // 使用数据库分页
      let historiesPage = try await query.paginate(PageRequest(page: input.page, per: input.per))

      // 转换为响应格式
      let historyResponses = historiesPage.items.map { history in
        HistoryResponse(
          id: history.id,
          progress: history.progress,
          duration: history.duration,
          isFinished: history.isFinished,
          lastListenedAt: history.lastListenedAt,
          createdAt: history.createdAt,
          listenedDuration: history.listenedDuration,
          deleteStatus: history.deleteStatus,
          deletedAt: history.deletedAt,
          listenStatus: history.listenStatus,
          completedCount: history.completedCount,
          audio: AudioSearchResponse(
            id: history.audio.id,
            title: history.audio.title,
            description: history.audio.description,
            duration: history.audio.duration,
            cover: history.audio.cover,
            author: history.audio.author,
            url: history.audio.url,
            size: history.audio.size,
            albumID: history.audio.$album.id,
            albumTitle: history.audio.album.title,
            albumCover: history.audio.album.cover,
            createdAt: history.audio.createdAt,
            updatedAt: history.audio.updatedAt
          )
        )
      }

      let response = Page(
        items: historyResponses,
        metadata: historiesPage.metadata
      )

      return try await response.encodeResponse(for: req)
    }
  }

  // MARK: - 删除单条历史记录
  @Sendable
  func softDeleteHistory(req: Request) async throws -> BasicResponse {
    let userID = try req.authenticatedUserID
    guard let historyID = req.parameters.get("historyID", as: UUID.self) else {
      throw Abort(.badRequest, reason: "Invalid history ID")
    }
    guard
      let history = try await History.query(on: req.db)
        .filter(\.$id == historyID)
        .filter(\.$user.$id == userID)
        .filter(\.$deleteStatus == .normal)
        .first()
    else {
      throw Abort(.notFound, reason: "未找到听力记录")
    }
    try await History.query(on: req.db)
      .filter(\.$user.$id == userID)
      .filter(\.$audio.$id == history.$audio.id)
      .filter(\.$deleteStatus == .normal)
      .set(\.$deleteStatus, to: .softDeleted)
      .set(\.$deletedAt, to: Date())
      .update()

    // 清理缓存
    try await invalidateRelatedCaches(
      audioID: history.$audio.id,
      albumID: try await history.$audio.get(on: req.db).$album.id,
      userID: userID,
      req: req
    )

    return BasicResponse(success: true, message: "删除成功")
  }

  // MARK: - 批量删除听力记录
  @Sendable
  func batchSoftDeleteHistories(req: Request) async throws -> BasicResponse {
    print("batchSoftDeleteHistories")
    let userID = try req.authenticatedUserID
    let input = try req.content.decode(BatchDeleteHistoriesRequest.self)
    return try await req.db.transaction { database in
      if input.deleteAll == true {
        try await History.query(on: database)
          .filter(\.$user.$id == userID)
          .filter(\.$deleteStatus == .normal)
          .set(\.$deleteStatus, to: .softDeleted)
          .set(\.$deletedAt, to: Date())
          .update()
      } else if let albumID = input.albumID {
        try await History.query(on: database)
          .filter(\.$user.$id == userID)
          .filter(\.$deleteStatus == .normal)
          .join(Audio.self, on: \History.$audio.$id == \Audio.$id)
          .filter(Audio.self, \.$album.$id == albumID)
          .set(\.$deleteStatus, to: .softDeleted)
          .set(\.$deletedAt, to: Date())
          .update()
      } else if !input.historyIDs.isEmpty {
        let targetHistories = try await History.query(on: database)
          .filter(\.$id ~~ input.historyIDs)
          .filter(\.$user.$id == userID)
          .all()
        let audioIDs = targetHistories.map { $0.$audio.id }
        try await History.query(on: database)
          .filter(\.$user.$id == userID)
          .filter(\.$audio.$id ~~ audioIDs)
          .filter(\.$deleteStatus == .normal)
          .set(\.$deleteStatus, to: .softDeleted)
          .set(\.$deletedAt, to: Date())
          .update()
      } else {
        throw Abort(.badRequest, reason: "Invalid request")
      }

      return BasicResponse(success: true, message: "删除成功")
    }
  }

  // MARK: - 获取未完成的音频
  @Sendable
  func getUnfinishedAudios(req: Request) async throws -> [Audio] {
    let userID = try req.authenticatedUserID
    let unfinishedHistories = try await History.query(on: req.db)
      .filter(\.$user.$id == userID)
      .filter(\.$isFinished == false)
      .filter(\.$audio.$id != Self.systemAudioID)  // 过滤掉系统音频
      .with(\.$audio)
      .sort(\.$lastListenedAt, .descending)
      .all()
    return unfinishedHistories.map { $0.audio }
  }

  // MARK: - 获取音频的最新历史记录(用于继续播放)
  //	@Sendable
  //	func getRecentHistoryByAudio(req: Request) async throws -> History {
  //		let userID = try req.authenticatedUserID
  //		guard let audioID = req.parameters.get("audioID", as: UUID.self) else {
  //			throw Abort(.badRequest, reason: "Invalid audio ID")
  //		}
  //		guard let history = try await History.query(on: req.db)
  //			.filter(\.$user.$id == userID)
  //			.filter(\.$audio.$id == audioID)
  //			.sort(\.$lastListenedAt, .descending)
  //			.first()
  //		else {
  //			throw Abort(.notFound, reason: "未找到听力记录")
  //		}
  //		return history
  //	}

  // 添加新的处理函数
  @Sendable
  func getAudioCount(req: Request) async throws -> AudioCountResponse {
    let userID = try req.authenticatedUserID
    
    guard let db = req.db as? MySQLDatabase else {
      throw Abort(.internalServerError, reason: "Database must be MySQL")
    }
    
      // Get today's start and end dates
    let calendar = Calendar.mondayFirst
    let todayStart = calendar.startOfDay(for: Date())
    let tomorrowStart = calendar.date(byAdding: .day, value: 1, to: todayStart)!
    
      // Query for today's distinct audio count
    let todayQuery = db.sql().raw(
    """
    SELECT COUNT(DISTINCT audio) as count
    FROM histories
    WHERE user = \(bind: userID)
    AND listenedDuration > 0
    AND audio != \(bind: Self.systemAudioID)
    AND lastListenedAt >= \(bind: todayStart)
    AND lastListenedAt < \(bind: tomorrowStart)
    """)
    
      // Query for all-time distinct audio count (existing query)
    let allTimeQuery = db.sql().raw(
    """
    SELECT COUNT(DISTINCT audio) as count
    FROM histories
    WHERE user = \(bind: userID)
    AND listenedDuration > 0
    AND audio != \(bind: Self.systemAudioID)
    """)
    
      // Query for completed count (existing query)
    let completedQuery = db.sql().raw(
    """
    SELECT COUNT(DISTINCT audio) as count
    FROM histories
    WHERE user = \(bind: userID)
    AND isFinished = true
    AND audio != \(bind: Self.systemAudioID)
    """)
    
    struct AudioCount: Decodable {
      let count: Int
    }
    
      // Execute all queries
    let todayResult = try await todayQuery.first(decoding: AudioCount.self)
    let allTimeResult = try await allTimeQuery.first(decoding: AudioCount.self)
    let completedResult = try await completedQuery.first(decoding: AudioCount.self)
    
      // Update the response struct to include today's count
    return AudioCountResponse(
      totalListened: allTimeResult?.count ?? 0,
      totalCompleted: completedResult?.count ?? 0,
      todayListened: todayResult?.count ?? 0
    )
  }


  @Sendable
  func getRecentCovers(req: Request) async throws -> [RecentCoverResponse] {
    let userID = try req.authenticatedUserID
    req.logger.info("开始获取用户[\(userID)]的最近封面数据")

    // 获取用户最近的10条历史记录
    let recentHistories = try await History.query(on: req.db)
      .filter(\.$user.$id == userID)
      .filter(\.$deleteStatus == .normal)
      .with(\.$audio) { audio in
        audio.with(\.$album)
      }
      .sort(\.$lastListenedAt, .descending)
      .limit(5)
      .all()

    req.logger.info("查询到用户[\(userID)]的最近历史记录数量: \(recentHistories.count)")

    let response = recentHistories.map { history in
      RecentCoverResponse(
        id: history.id!,
        audioID: history.audio.id!,
        audioCover: history.audio.cover ?? "",
        albumID: history.audio.$album.id,
        albumCover: history.audio.album.cover ?? "",
        lastListenedAt: history.lastListenedAt ?? Date()
      )
    }

    // 记录每个封面的详细信息
    for (index, cover) in response.enumerated() {
      req.logger.info(
        "封面[\(index+1)]: ID=\(cover.id), 音频ID=\(cover.audioID), 专辑ID=\(cover.albumID), 最后收听时间=\(cover.lastListenedAt)"
      )
    }

    // 记录完整的返回数据
    if let jsonData = try? JSONEncoder().encode(response),
      let jsonString = String(data: jsonData, encoding: .utf8)
    {
      req.logger.debug("完整的返回数据: \(jsonString)")
    }

    req.logger.info("返回最近封面数据，共\(response.count)条")
    return response
  }
}

// 在适当的位置添加这个扩展
extension AudioCountResponse: CustomStringConvertible {
  var description: String {
    return
      "AudioCountResponse(totalListened: \(totalListened), totalCompleted: \(totalCompleted), 完成率: \(Double(totalCompleted) / Double(max(1, totalListened)) * 100)%)"
  }
}

extension HistoryController {
  private func invalidateRelatedCaches(
    audioID: UUID,
    albumID: UUID,
    userID: UUID,
    req: Request
  ) async throws {
    // 清理单个音频的历史记录缓存
    let audioHistoriesKey = CacheConfig.KeyBuilder.audioHistories(
      audioIds: [audioID],
      userID: userID
    )
    try await req.cache.delete(forKey: audioHistoriesKey, on: req.redis)

    // 清理专辑相关的缓存
    let albumAudiosKey = CacheConfig.KeyBuilder.audiosByAlbum(
      albumID: albumID,
      userID: userID
    )
    try await req.cache.delete(forKey: albumAudiosKey, on: req.redis)
  }
}
